package com.ms.service.db.jedis;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisException;

import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.game.utility.RoundRobinSelector;

public class JedisManager {
	
	/**
	 * log
	 */
	private static Log log = LogFactory.getLog(JedisManager.class);
	
	private static HashMap<String,JedisDatabase> JedisDatabases = new HashMap<String,JedisDatabase>(); 

	/**
	 * 用來暫存 db log 的 redis server map<br>
	 * log redis 編號 \ JedisDatabase
	 */
	private static HashMap<Integer,JedisDatabase> jedisLogDatabases = new HashMap<Integer,JedisDatabase>();
	
	/**
	 * 所有的 log redis 編號列表<br>
	 * Integer: log redis 編號
	 */
	private static ArrayList<Integer> logRedisNumList = new ArrayList<Integer>();
	
	/**
	 * 用輪詢的方式取得  log redis 資源<br>
	 * Integer: log redis 編號
	 */
	private static RoundRobinSelector<Integer> logRedisSelecter = new RoundRobinSelector<Integer>();

	/**
	 * 設定檔中 player data redis 相關設定項目的開頭字串
	 */
	public static final String PLAYER_DATA_PROP_KEY = "PlayerDataRedis";

	/**
	 * 用來存 player 資料的的 redis server map<br>
	 * redis name \ JedisDatabase
	 */
	private static HashMap<String, JedisDatabase> jedisPlayerDatabases = new HashMap<String, JedisDatabase>();

	/**
	 * 初始化
	 */
	public static void initial(String moduleName)
	{	
		JedisDatabase jedisDatabase = JedisDatabases.get(moduleName);
		if(jedisDatabase != null){
			if(log.isInfoEnabled()){
				log.info("now nodule name is " + moduleName);
			}
			jedisDatabase.initial();
		}
    }
	
	/**
	 * 初始化設定
	 */
	public static void initialJedisConfiguration(Properties pro){
		
		try{
			Class.forName("redis.clients.jedis.JedisPoolConfig");
		}catch(ClassNotFoundException e){
			if(log.isErrorEnabled()){
				log.error("Jedis library not found !!!!" , e);
			}
		}
	     
	     int i;
	     int jedisNumber = Integer.valueOf(pro.getProperty("Jedis_number"));
	     
	     for(i = 1 ; i <= jedisNumber ; i++){
	    	 String acceptModuleName = "Jedis_" + i + "_Accept_Module";
	    	 
	    	 if(pro.getProperty(acceptModuleName) == null){
	    		 break;
	    	 }
	    	 JedisDatabase jedisDatabase = new JedisDatabase();
	    	 
	    	 JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		     jedisPoolConfig.setMaxWaitMillis( Long.valueOf(pro.getProperty("Jedis_" + i + "_Max_Wait_Millis")) ); //最大等待時間
		     jedisPoolConfig.setMaxTotal( Integer.valueOf(pro.getProperty("Jedis_" + i + "_Max_Total")) ); //最大分配數
		     jedisPoolConfig.setMaxIdle( Integer.valueOf(pro.getProperty("Jedis_" + i + "_Max_Total")) ); //最大閒置個數
		     jedisPoolConfig.setMinIdle( Integer.valueOf(pro.getProperty("Jedis_" + i + "_Max_Total")));
		     String host = pro.getProperty("Jedis_" + i + "_IP_Port");
	    	 
	    	 
	    	 jedisDatabase.initialJedisConfiguration(jedisPoolConfig , host);
	    	 String[] acceptModules = pro.getProperty(acceptModuleName).split(",");
	    	 for(String moduleName:acceptModules){
	    		 if(JedisDatabases.get(moduleName) == null){
	    			 JedisDatabases.put(moduleName, jedisDatabase);
	    		 }
	    	 }
	    	 
	     }
	     
	     
	     // 初始寫入暫存 log 的 redis
	     int logRedisNum = 1;
	     
	     while(true){
			String redisHost = pro.getProperty("LogToRedis_" + logRedisNum + "_IP_Port");
			
			// 取不到 host 就結束
			if(!GameUtility.isHaveString(redisHost)){
				break;
			}
			
	    	 JedisDatabase jedisDatabase = new JedisDatabase();
	    	 
	    	 JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		     jedisPoolConfig.setMaxWaitMillis( Long.valueOf(pro.getProperty("LogToRedis_Max_Wait_Millis"))); //最大等待時間
		     jedisPoolConfig.setMaxTotal( Integer.valueOf(pro.getProperty("LogToRedis_Max_Total"))); //最大分配數
		     jedisPoolConfig.setMaxIdle( Integer.valueOf(pro.getProperty("LogToRedis_Max_Idle"))); //最大閒置個數
		     jedisPoolConfig.setMinIdle( Integer.valueOf(pro.getProperty("LogToRedis_Min_Idle")));
	    	 
	    	 jedisDatabase.initialJedisConfiguration(jedisPoolConfig , redisHost);
	    	 
	    	 jedisLogDatabases.put(logRedisNum, jedisDatabase);
	    	 logRedisNumList.add(logRedisNum);
	    	 
	    	 logRedisNum++;
	     }
	     
	     logRedisSelecter.setSelectObjectList(logRedisNumList);

	     // 初始化暫存玩家資料用的 redis
	     initialPlayerDataJedis(pro);
	}
	
	/**
	 * 初始化暫存玩家資料用的 redis
	 * 
	 * @param pro
	 */
	private static void initialPlayerDataJedis(Properties pro) {
		int playerDataRedisNum = 1;

		while (true) {
			String propKeyNum = PLAYER_DATA_PROP_KEY + "_" + playerDataRedisNum;
			String redisHost = pro.getProperty(propKeyNum + "_IP_Port");

			// 取不到 host 就結束
			if (!GameUtility.isHaveString(redisHost)) {
				break;
			}

			JedisDatabase jedisDatabase = new JedisDatabase();

			JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
			// 最大等待時間
			jedisPoolConfig.setMaxWaitMillis(Long.valueOf(pro.getProperty(PLAYER_DATA_PROP_KEY
					+ "_Max_Wait_Millis")));
			// 最大分配數
			jedisPoolConfig.setMaxTotal(Integer.valueOf(pro.getProperty(PLAYER_DATA_PROP_KEY + "_Max_Total")));
			// 最大閒置個數
			jedisPoolConfig.setMaxIdle(Integer.valueOf(pro.getProperty(PLAYER_DATA_PROP_KEY + "_Max_Idle")));
			jedisPoolConfig.setMinIdle(Integer.valueOf(pro.getProperty(PLAYER_DATA_PROP_KEY + "_Min_Idle")));

			jedisDatabase.initialJedisConfiguration(jedisPoolConfig, redisHost);

			jedisPlayerDatabases.put(propKeyNum, jedisDatabase);

			playerDataRedisNum++;
		}
	}

	/**
	 * 取得放角色資料的 redis resource
	 * 
	 * @param resourceName
	 *            redis resource name, 參考設定檔中 {@value #PLAYER_DATA_PROP_KEY}
	 *            開頭的設定項目
	 * @return
	 */
	public static synchronized Jedis getPlayerDataResource(String resourceName){
		Jedis jedis = null;
		
		JedisDatabase jedisDatabase = jedisPlayerDatabases.get(resourceName);
		if(jedisDatabase != null){
			jedis = jedisDatabase.getResource();
		}
		return jedis;
    }

	/**
	 * 歸還放角色資料的 redis resource
	 * 
	 * @param resourceName
	 *            redis resource name, 參考設定檔中 {@value #PLAYER_DATA_PROP_KEY}
	 *            開頭的設定項目
	 * @param jedis
	 */
	public synchronized static void returnPlayerDataResource(String resourceName, Jedis jedis) {

		JedisDatabase jedisDatabase = jedisPlayerDatabases.get(resourceName);
		if (jedisDatabase != null) {
			jedisDatabase.returnResource(jedis);
		}
	}

	/**
	 * 請使用 RedisResourceName </br>
	 * @see RedisResourceName
	 * @param moduleName
	 * @return
	 */
	public static synchronized Jedis getResource(String resource){
		Jedis jedis = null;
		
		JedisDatabase jedisDatabase = JedisDatabases.get(resource);
		if(jedisDatabase != null){
			jedis = jedisDatabase.getResource();
		}
		return jedis;
    }
    
	@SuppressWarnings("deprecation")
	public synchronized static void returnResource(String moduleName, Jedis jedis){
		
		JedisDatabase jedisDatabase = JedisDatabases.get(moduleName);
		if(jedisDatabase != null){
			jedisDatabase.returnResource(jedis);
		}
    }

	public static synchronized Jedis getLogResource(int logRedisNum) {
		Jedis jedis = null;

		JedisDatabase jedisDatabase = jedisLogDatabases.get(logRedisNum);
		if (jedisDatabase != null) {
			jedis = jedisDatabase.getResource();
		}

		return jedis;
	}
	
	public static int getLogRedisCount(){
		return logRedisNumList.size();
	}

	/**
	 * 用輪詢的方式取得  log redis 編號
	 * @return
	 */
	public static synchronized int getNextLogRedisNum() {
		int logRedisNum = logRedisSelecter.getNext();
		return logRedisNum;
	}

	public synchronized static void returnLogResource(int logRedisNum, Jedis jedis) {
		JedisDatabase jedisDatabase = jedisLogDatabases.get(logRedisNum);
		if (jedisDatabase != null) {
			jedisDatabase.returnResource(jedis);
		}
	}

    public synchronized static void destroy(String moduleName){
    	JedisDatabase jedisDatabase = JedisDatabases.get(moduleName);
		if(jedisDatabase != null){
			jedisDatabase.destroy();
		}
    }
    
    
    /**
     * 連線錯誤
     */
    protected static boolean handleJedisException(String moduleName, JedisException jedisException) {
    	JedisDatabase jedisDatabase = JedisDatabases.get(moduleName);
		if(jedisDatabase != null){
			return jedisDatabase.handleJedisException(jedisException);
		}
    	return false;
    }
    
    protected static void closeResource(String moduleName, Jedis jedis, boolean conectionBroken) {
    	
    	JedisDatabase jedisDatabase = JedisDatabases.get(moduleName);
		if(jedisDatabase != null){
			jedisDatabase.closeResource(jedis , conectionBroken);
		}
    }

	public static HashMap<String, JedisDatabase> getJedisPlayerDatabasesMap() {
		return jedisPlayerDatabases;
	}
}
